package com.zg.common.core.service.support;

import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.support.LambdaMeta;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;

import java.io.Serializable;
import java.lang.invoke.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 通用 Lambda 工具类 - 通过反射生成符合 MyBatis-Plus 规范的 Lambda
 */
public class MyLambdaUtils {
    private static final Map<Class<?>, Map<String, SFunction<?, ?>>> FUNCTION_CACHE = new ConcurrentHashMap<>();
    private static final Map<Class<?>, Map<String, SFunction<?, ?>>> CACHE = new ConcurrentHashMap<>();

    public static <T, R> SFunction<T, R> getFunction(Class<T> entityClass, String fieldName) {
        Map<String, SFunction<?, ?>> classCache = FUNCTION_CACHE.computeIfAbsent(entityClass, k -> new HashMap<>());
        if (classCache.containsKey(fieldName)) {
            return (SFunction<T, R>) classCache.get(fieldName);
        }

        //String getterMethodName = getGetterMethodName(entityClass, fieldName);
        try {
            //Method getterMethod = entityClass.getMethod(getterMethodName);
            Method getter = getGetterMethod(entityClass, fieldName);
            SFunction<T, R> function = createSerializableLambda(entityClass, getter);

//            LambdaMeta lambdaMeta = LambdaUtils.extract(function);
//            SerializedLambda serializedLambda = SerializedLambda.resolve(function);
//            LambdaUtils.installCache(function);
//            System.out.println("Generated Lambda method: " + lambdaMeta.getImplMethodName());
//            System.out.println(lambdaMeta.getInstantiatedClass());
//            System.out.println(lambdaMeta.getImplMethodName());

            classCache.put(fieldName, function);
            return function;
        } catch (Exception e) {
            throw new RuntimeException("Failed to create SFunction for field: " + fieldName, e);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }


    private static <T> Method getGetterMethod(Class<T> entityClass, String fieldName)
            throws NoSuchMethodException {
        String getterName = "get" + capitalize(fieldName);
        try {
            return entityClass.getMethod(getterName);
        } catch (NoSuchMethodException e) {
            String isGetterName = "is" + capitalize(fieldName);
            return entityClass.getMethod(isGetterName);
        }
    }


    private static <T, R> SFunction<T, R> createSerializableLambda(Class<T> entityClass, Method getter)
            throws Throwable {
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodType methodType = MethodType.methodType(getter.getReturnType(), entityClass);

        // 使用altMetafactory并添加FLAG_SERIALIZABLE标志
        CallSite site = LambdaMetafactory.altMetafactory(
                lookup,
                "apply",
                MethodType.methodType(SFunction.class),
                MethodType.methodType(Object.class, Object.class), // 函数式接口方法签名
                lookup.unreflect(getter),                         // 方法句柄
                methodType,                                        // 实现方法类型
                LambdaMetafactory.FLAG_SERIALIZABLE,              // 可序列化标志
                0,                                                 // 桥接方法数量
                new MethodType[0]                                  // 桥接方法类型数组
        );


        // 确保返回的 Lambda 表达式实现了 Serializable 接口
        SFunction<T, R> function = (SFunction<T, R> & Serializable) site.getTarget().invokeExact();

        return function;

    }

    private static String getGetterMethodName(Class<?> entityClass, String fieldName) {
        try {
            Field field = entityClass.getDeclaredField(fieldName);
            if (boolean.class.equals(field.getType()) || Boolean.class.equals(field.getType())) {
                return fieldName.startsWith("is") ? fieldName : "is" + capitalize(fieldName);
            } else {
                return "get" + capitalize(fieldName);
            }
        } catch (NoSuchFieldException e) {
            throw new RuntimeException("Field not found: " + fieldName, e);
        }
    }

    private static String capitalize(String str) {
        if (str == null || str.isEmpty()) return str;
        return Character.toUpperCase(str.charAt(0)) + str.substring(1);
    }
}